;主引导程序
;----------------
%include "boot.inc"
SECTION MBR vstart=0x7c00
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov sp,0x7c00 ;初始化栈指针
    mov ax,0xb800 ;现存文本内存地址0xb8000, 将b800作为ie段地址
    mov gs,ax
    
    ;清屏 0x06号功能
    ;AH 功能号 = 0x06
    ;AL 上卷的行数 (0表示全部)
    ;BH 上卷行属性
    ;(CL, CH) = 窗口左上角位置(X,Y)
    ;(DL, DH) = 窗口右下角位置(X,Y)
    mov     ax, 0x0600
    mov     bx, 0x0700
    mov     cx, 0x0000          ;左上角(0,0)
    mov     dx, 0x184f          ;右下角(0x4f=79,0x18=24)
                                ;VGA文本模式中共25行,一行80字符,下标从0开始
    int 0x10                    ;进入中断,执行清屏

    ;写入显存,显示字符
    mov byte [gs:0x00],'1'      ;byte 指定操作数所占空间(1字节), 将字符"1"的ASCII码写入gs:0x00为起始大小为1字节的内存中
    mov byte [gs:0x01],0xA4     ;A表示绿色背景闪烁,4表示前景色红色

    mov byte [gs:0x02],' '
    mov byte [gs:0x03],0xA4

    mov byte [gs:0x04],'M'
    mov byte [gs:0x05],0xA4

    mov byte [gs:0x06],'B'
    mov byte [gs:0x07],0xA4

    mov byte [gs:0x08],'R'
    mov byte [gs:0x09],0xA4

    
    mov eax,    LOADER_START_SECTOR ;起始扇区的lba地址
    mov bx,     LOADER_BASE_ADDR    ;写入的地址
    mov cx,     4                   ;待读入的扇区数,简单的loader,不会超过512字节
    call rd_disk_m_16               ;以下读取程序的起始部分(一个扇区)

    jmp LOADER_BASE_ADDR + 0x300;MBR交出接力棒


;----------------
;功能:读取硬盘n个扇区
rd_disk_m_16:;在16位模式下读硬盘
;----------------
    mov esi,    eax ;备份eax, al在out中会用到,会影响到低8位的ax寄存器
    mov di,     cx  ;备份cx

;读写硬盘
;1.设置要读取的扇区数
    mov dx, 0x01f2  ;dx存储0x01f2这个端口号,见表3-17,p126页,是sector count寄存器
    mov al, cl
    out dx, al  ;读取的扇区数,把al写入dx所存储的端口号对应的端口中去

    mov eax,    esi ;恢复ax

;2.将LBA的地址存入0x1f3~0x1f6

    ;7~0位写入0x1f3 LBA LOW
    mov dx, 0x01f3
    out dx, al

    ;15~8
    mov dx, 0x01f4
    mov cl, 8
    shr eax, cl ;逻辑右移
    out dx, al

    ;23~16
    mov dx, 0x01f5
    shr eax, cl
    out dx, al

    shr eax, cl
    and al, 0x0f ;24~27位
    or al,  0xe0 ;设置7-4位为1110,表示lba模式
    mov dx, 0x01f6 ;device
    out dx, al

;3.向0x1f7端口写入读命令,0x20
    mov dx, 0x01f7 ;command
    mov al, 0x20
    out dx, al ;写入后,硬盘开始工作了

;4.检测硬盘状态
.not_ready:
    ;同一端口,写入时表示写入命令字,读时表示读入硬盘状态
    nop ;空操作, 为了增加延迟, 相当于小sleep
    in al, dx ;读取0x1f7端口给出的硬盘状态
    and al,0x88 ; 第三位为1表示硬盘准备好传输数据
                ; 第7位为1表示硬盘忙
                ; 0x88 = 10001000
    cmp al,0x08 ; 减法,判断3位是否为1,al-0x08
                ; 减法操作会影响ZF,CF,PF等, 借助ZF判断cmp结果
    jnz .not_ready  ;0x08 - 0x08 = 0 -> ZF = 1
                    ;0x08 - 0x80 != 0 -> ZF = 0
                    ;jnz: 若ZF=0,即减法结果不为0 再重新检测,等待

;5.从0x1f0端口读取数据,data寄存器是16位,每次in只能读入2字节,根据读入数据总量(扇区数*512字节)决定in指令次数

    mov ax, di
    mov dx, 256
    mul dx ;将256与ax=di=待读入的扇区数相乘,得到in操作次数,乘法高16位在dx,低16位在ax,显然此时高位dx=0,
    mov cx, ax ;cx为循环次数,对应loop
    mov dx, 0x01f0 ;DATA端口

.go_on_read:
    in ax, dx
    mov [bx], ax
    add bx, 2
    loop .go_on_read
    ret ;对应之前的call,返回之前call的位置





    times 510-($-$$) db 0 ;填充剩余空间
    db 0x55,0xaa ;MBR标志